class_name Ball
extends AnimatableBody2D



const STATE_CARRIED := "carried"
const STATE_FREEFORM := "freeform"
const STATE_SHOT := "shot"

const ANIMATION_IDLE := "idle"
const ANIMATION_ROLL := "roll"
const ANIMATION_ROLL_BACK := "roll_back"


@onready var ball_sprite: Sprite2D = $BallSprite
@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var player_detection_area: Area2D = $PlayerDetectionArea
@onready var state_matchine: NodeStateMachine = $StateMachine


## 球在空中的摩擦力
@export var friction_air: float = 35.0
## 球在地面的摩擦力
@export var friction_ground: float = 75.0

## 处于携带状态时，与带球者的偏移量
@export var offset_carrier: Vector2 = Vector2(10, 2)
## 运球时的频率
@export var dribble_frequency: float = 10.0
## 运球时的强度
@export var dribble_intensity: float = 3.0
## 射门时球在y轴的缩放，以营造速度感
@export_range(0.0, 1.0) var shot_scale: float = 0.8
## 射门时球跃起的最大高度
@export var shot_height: float = 5.0
## 射门时球的重力变化曲线
@export_exp_easing var shot_gravity_curve: float = 2.0
## 弹跳因子，球在自由状态落地时根据弹跳因子衰减后反弹
@export var bounciness: float = 0.8
## 与场地物体碰撞后的弹性系数
@export var pitch_bounciness: float = 0.8
## 高空传球的距离阈值，只有队友位置超过此阈值才会传高空球，否则传地面球
@export var high_passing_distance: float = 100
## 可在空中交互的高度范围，使用 Vector2 类型，x 表示最小值，y 表示最大值，此为正值
@export var air_interactive_height: Vector2 = Vector2(10, 40)


# 当高度为零时，弹跳时高度速度的边界，防止无限弹跳
const HEIGHT_VELOCITY_BOUNDARY: float = 1.0
# 球超过此高度才会进入控球状态
const CONTROL_BALL_HEIGHT := 15.0


# 速度
var velocity: Vector2 = Vector2.ZERO
# 球的高度
var height: float = 0.0
# 球的高度速度，当前球落下时值为负数，弹起时值为正数
var height_velocity: float = 0.0

# 带球者
var carrier: Player = null


func _ready():
	player_detection_area.body_entered.connect(on_player_enter)


## 球是否可在空中交互
func can_air_interactive() -> bool:
	if state_matchine.current_node_state_name != STATE_FREEFORM:
		return false
	var ball_height := absf(ball_sprite.position.y)
	return ball_height >= air_interactive_height.x and ball_height <= air_interactive_height.y


## 球停止运动
func stop():
	velocity = Vector2.ZERO



## 球进行移动并且碰撞后反弹
func move_and_bounce(delta: float):
	var collision := move_and_collide(velocity * delta)
	if collision:
		velocity = velocity.bounce(collision.get_normal()) * pitch_bounciness
		state_matchine.transition_to(STATE_FREEFORM)



## 射门
func shoot(shot_power: float, max_shot_power: float, shot_direction: Vector2, ball_height: float):
	velocity = shot_power * shot_direction

	var data := BallShotData.new()
	data.shot_power = shot_power
	data.max_shot_power = max_shot_power
	data.ball_height = ball_height
	state_matchine.transition_to(STATE_SHOT, data)
	clear_carrier()


## 传球 [br]
##
## 传球的速度的公式推导如下： [br]
## 设：质量为 m = 1，加速度为 a，摩擦力为 f，速度为 v，时间为 t，距离为 x [br]
## 则有如下公式： [br]
## -f = m * a 即 a = -f  (因执行的是减速，摩擦力为负数) [br]
## 加速度 a = Δv / Δt 即对 t -> t0 和 v -> v0 求积分得：v = a * t + v0 （t0 = 0） [br]
## 速度 v = Δx / Δt 即对 x -> x0 和 t -> t0 求积分得：x = 1/2 * a * t^2 + v0 * t （x0 = 0，t0 = 0） [br]
## 距离 x = (v0 + v1) / 2 * t  得：t = 2 * x * v0 （因最终速度到达0，故 v1 = 0） [br]
## 初始速度 v0 = sqrt(2 * f * x)
func pass_to(target_position: Vector2):
	var pass_direction := position.direction_to(target_position)
	var pass_distance := position.distance_to(target_position)
	var intensity := sqrt(2 * pass_distance * friction_ground)

	if pass_distance > high_passing_distance:
		# TODO 计算高空传球需要的高度速度
		height_velocity = GlobalConstants.BALL_GRAVITY * pass_distance / (2 * intensity)

	velocity = pass_direction * intensity

	clear_carrier()
	state_matchine.transition_to(STATE_FREEFORM)


# 处理重力
func process_gravity(delta : float, gravity: float = GlobalConstants.BALL_GRAVITY):
	ball_sprite.position.y = -height

	if height > 0.0:
		height_velocity -= gravity * delta
		height += height_velocity
	if height < 0.0:
		height = 0.0
		height_velocity = 0.0



# 处理反弹
func process_bounce(delta : float, bounciness_value: float, gravity: float = GlobalConstants.BALL_GRAVITY):
	if height < 0:
		height = 0.0
	ball_sprite.position.y = -height

	if height_velocity > 0:
		height_velocity -= gravity * delta
		height += height_velocity

	elif height_velocity < 0:
		if height > 0:
			height_velocity -= gravity * delta
			height += height_velocity
		elif height < GlobalConstants.TINY:
			height_velocity = -height_velocity * bounciness_value
			height = 0.0

	# 当高度接近零时，同时高度速度接近边界值时，阻止继续弹跳
	if absf(height) < GlobalConstants.TINY and absf(height_velocity) < HEIGHT_VELOCITY_BOUNDARY:
		height = 0.0
		height_velocity = 0.0




# 重置高度
func reset_height():
	height = 0.0
	height_velocity = 0.0
	ball_sprite.position.y = height


## 是否被球员持有
func is_carried() -> bool:
	return carrier != null

## 是否被指定的球员持有
func is_carried_by(carried_player: Player) -> bool:
	return carrier and carrier == carried_player


## 清空带球者
func clear_carrier():
	carrier = null


func on_player_enter(body: Node2D):
	var player: Player = body as Player
	if not player:
		return
	# 球的高度超过了可交互的高度时，不会持有球
	var ball_height := absf(ball_sprite.position.y)
	if ball_height > air_interactive_height.y:
		return
	carrier = player
	state_matchine.transition_to(STATE_CARRIED)
	# 球的高度超过一定时球员才会进入控球状态
	if ball_height >= CONTROL_BALL_HEIGHT:
		player.control_ball()
